﻿using ImportPlcPointPosition.Extensions;
using SqlSugar.Extensions;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace ImportPlcPointPosition.Controls.Base
{
    /// <summary>
    /// 点位配置控件基类
    /// </summary>
    public class BasePointConfigContol : BaseUserControl
    {
        /// <summary>
        /// 展开PointData
        /// </summary>
        /// <returns></returns>
        public List<ImportData> ExpandPointDataToList(BindingList<ImportData> PointDatas)
        {
            var all = new List<ImportData>();
            var child = PointDatas.ToList();
            int maxorder = 0;
            return BuildDataList(all, child, ref maxorder);
        }

        /// <summary>
        /// 偏移量计算 精度：10 ，代表需要乘以10再除以10，计算Lenth时 先乘以10 
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        /// <exception cref="NotImplementedException"></exception>
        public decimal GetVariableOffset(ImportData data) {
            switch (data.DataType.ToLower()) {
                case "struct":
                    data.ParseType = "struct";
                    return 0;
                case "int":
                    data.ParseType = "int16";
                    return 2;
                case "word":
                    data.ParseType = "word";
                    return 2;

                case "dint":
                    data.ParseType = "int32";
                    return 4;
                case "bool":  //不支持bool，连续bool的偏移量示例 200.0，200.1
                    data.ParseType = "bool";
                    return 0.1m;
                case "string":
                    data.ParseType = "string";
                    return 256;
                case "real":
                    data.ParseType = "float";
                    return 4;
                case "lreal":
                    data.ParseType = "double";
                    return 8;
                case "byte":
                    data.ParseType = "byte";
                    return 1;
                case "dt":
                    data.ParseType = "dt";
                    return 8;
                case "dtl":
                    data.ParseType = "dtl";
                    return 12;
                default:
                    var dataType = data.DataType.ToLower();
                    if (dataType.StartsWith("string[")) {
                        data.ParseType = "string";
                        return Regex.Match(dataType, "[0-9]+").Value.ObjToInt() + 2;
                    }
                    if (dataType.StartsWith("array[") && dataType.Contains("of")) {
                        //Array[0..63] of Word
                        // Debug.WriteLine(dataType);
                        if (dataType.Contains("string")) {
                        }
                        data.ParseType = data.DataType;
                        var matches = Regex.Matches(dataType, "[0-9]+");
                        if (matches.Count == 2) {
                            var Start = matches[0].Value.ObjToInt();
                            var End = matches[1].Value.ObjToInt();
                            decimal sizelenth = End - Start + 1;

                            var arrayType = dataType.Split(' ')[2];


                            sizelenth = arrayType switch {
                                "byte" => sizelenth,
                                "bool" => GetBoolLenth(sizelenth),
                                "int" => sizelenth * 2,
                                "word" => sizelenth * 2,
                                "dword" => sizelenth * 4, //uint
                                "dint" => sizelenth * 4,
                                "real" => sizelenth * 4,
                                "lreal" => sizelenth * 8,
                                "dt" => sizelenth * 8,
                                "dtl" => sizelenth * 12,
                                _ => sizelenth
                            };

                            //


                            //数组需要补1到2个字节的偏移长度，必须是偶数
                            if (sizelenth % 2 != 0)
                                sizelenth++;
                            return sizelenth;
                        } else if (matches.Count == 3 && dataType.Contains("of string")) {
                            //字符串数组
                            var Start = matches[0].Value.ObjToInt();
                            var End = matches[1].Value.ObjToInt();
                            decimal sizelenth = End - Start + 1;
                            var arrayType = dataType.Split(' ')[2];

                            sizelenth = sizelenth * (matches[2].Value.ObjToInt() + 2);
                            if (sizelenth % 2 != 0)
                                sizelenth++;
                            return sizelenth;
                        }


                        return Regex.Match(dataType, "[0-9]+").Value.ObjToInt() + 2;
                    }

                    throw new NotImplementedException($"暂未支持类型 {dataType}");

            }
            //  数据类型：int\bool\string[20]\real\lreal
        }

        /// <summary>
        /// 获取类型占位空间
        /// </summary>
        /// <param name="ParseType"></param>
        /// <returns></returns>
        public decimal GetTypeOffset(string ParseType) {
            var offset = ParseType switch {
                "byte" => 1,
                "int" => 2,
                "word" => 2,
                "dword" => 4, //uint
                "dint" => 4,
                "real" => 4,
                "lreal" => 8,
                "dtl" => 12,
                "dt" => 8,
                "bool" => 0.1m,
                _ => 1
            };

            if (offset == 0 && ParseType.Contains("string")) {
                return Regex.Match(ParseType, "[0-9]+").Value.ObjToInt() + 2;
            }
            return offset;
        }


        /// <summary>
        /// 是否是Array类型
        /// </summary>
        /// <param name="ArrayType"></param>
        /// <param name="arrayLenth"></param>
        /// <param name="ParseType"></param>
        /// <returns></returns>
        public bool IsArrayType(string ArrayType, out int arrayLenth, out string ParseType)
        {
            ParseType = "";
            arrayLenth = 0;
            var dataType = ArrayType?.ToLower();
            if (dataType!=null && dataType.StartsWith("array[") && dataType.Contains("of"))
            {
                //Array[0..63] of Word
                var matches = Regex.Matches(dataType, "[0-9]+");
                if (matches.Count == 2)
                {
                    var Start = matches[0].Value.ObjToInt();
                    var End = matches[1].Value.ObjToInt();
                    decimal sizelenth = End - Start + 1;
                    arrayLenth = (int)sizelenth;
                    var arrayType = dataType.Split(' ')[2];
                    sizelenth = arrayType switch
                    {
                        "bool" => GetBoolLenth(sizelenth),
                        "int" => sizelenth * 2,
                        "word" => sizelenth * 2,
                        "dword" => sizelenth * 4, //uint
                        "dint" => sizelenth * 4,
                        "real" => sizelenth * 4,
                        "lreal" => sizelenth * 8,
                        "dtl" => sizelenth * 12,
                        _ => sizelenth
                    };
                    ParseType = arrayType;
                    return true;
                }
                else if (matches.Count == 3 && dataType.Contains("of string"))
                {
                    var arrayType = dataType.Split(' ')[2];
                    ParseType = arrayType;
                    var Start = matches[0].Value.ObjToInt();
                    var End = matches[1].Value.ObjToInt();
                    decimal sizelenth = End - Start + 1;
                    arrayLenth = (int)sizelenth;

                    return true;
                }
            }
            return false;
        }

        /// <summary>
        /// 转化为S7的对应类型
        /// </summary>
        /// <param name="data"></param>
        public string ConvertToS7Type(ImportData data)
        {
            string DataType = "";
            switch (data.ParseType)
            {
                case "struct":
                    DataType = "Struct";
                    break;
                case "int16":
                    DataType = "Int";
                    break;
                case "int32":
                    DataType = "DInt";
                    break;
                case "bool":
                    DataType = "Bool";
                    break;
                case "float":
                    DataType = "Real";
                    break;
                case "double":
                    DataType = "LReal";
                    break;
                case "dtl":
                    DataType = "DTL";
                    break;
                case "string":
                    DataType = $"string[{data.ByteLength - 2}]";
                    break;
                case "array":
                    DataType = $"array[{data.ByteLength - 2}]";
                    data.Operate = null;
                    break;
                default:
                    DataType = data.ParseType; //数据库只存了ParseType 
                    break;
            }
            return DataType;
        }

        /// <summary>
        /// 获取bool类型的长度
        /// </summary>
        /// <param name="sizelenth"></param>
        /// <returns></returns>
        private decimal GetBoolLenth(decimal sizelenth)
        {
            int d = (int)(sizelenth / 8);
            var yu = sizelenth % 8;
            if (yu == 0)
                return d;
            return d + yu / 10;
        }

        /// <summary>
        /// 构建List
        /// </summary>
        /// <param name="alls"></param>
        /// <param name="child"></param>
        /// <param name="orderNo"></param>
        /// <returns></returns>
        private List<ImportData> BuildDataList(List<ImportData> alls, List<ImportData> child, ref int orderNo)
        {
            foreach (var item in child)
            {
                alls.Add(item);
                item.OrderNo = ++orderNo;
                if (item.ParseType == "struct")
                {
                    //查找子元素
                }
                if (item.SubItems != null && !item.ParseType.IsS7Array() && item.SubItems.Count() > 0)
                {
                    BuildDataList(alls, item.SubItems.ToList(), ref orderNo);
                }
            }
            return alls.OrderBy(a => a.OrderNo).ToList();
        }

    }

}
